package com.github.yoojia.flow;

import com.github.yoojia.flow.impl.LocalDeviceEventDelivery;
import com.github.yoojia.flow.support.SnowflakeId;
import com.parkingwang.lang.Consumer;
import com.parkingwang.lang.Lazy;
import com.parkingwang.lang.Required;
import com.parkingwang.lang.data.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.github.yoojia.flow.util.Classes.findIfExists;

/**
 * @author 2017 Yoojia Chen (yoojiachen@gmail.com)
 */
public class NextFlow implements Context {

    private static final Logger LOGGER = LoggerFactory.getLogger(NextFlow.class);

    public static final String VERSION = "1.0.0";

    public static final String FIELD_NAME_PLUGINS = "plugins";
    public static final String FIELD_NAME_INTERCEPTORS = "interceptors";
    public static final String FIELD_NAME_TRANSFORMERS = "transformers";
    public static final String FIELD_NAME_HANDLERS = "handlers";
    public static final String FIELD_NAME_DELIVERIES = "deliveries";
    public static final String FIELD_NAME_DEVICES = "devices";
    public static final String FIELD_NAME_SYSTEM = "system";
    public static final String FIELD_NAME_IMPLEMENT = "implementation";
    public static final String FIELD_NAME_DISABLED = "disabled";

    protected final List<EventInterceptor> mInterceptors = new CopyOnWriteArrayList<>();
    protected final List<EventPipeline> mPipelines = new CopyOnWriteArrayList<>();
    protected final List<EventHandler> mHandlers = new CopyOnWriteArrayList<>();
    protected final List<EventDelivery> mDeliveries = new CopyOnWriteArrayList<>();
    protected final List<Plugin> mPlugins = new CopyOnWriteArrayList<>();
    protected final List<VirtualDevice> mDevices = new CopyOnWriteArrayList<>();

    protected final Dispatcher mDispatcher = new Dispatcher(mInterceptors, mPipelines, mHandlers, mDeliveries);

    protected final Config mGlobalConfig = new Config();
    protected final Config mSystemConfig = new Config();

    private final Lazy<SnowflakeId> mSnowflakeId = Lazy.from(() ->
            new SnowflakeId(mSystemConfig.getLong("snowflake.worker.id", 0),
                    mSystemConfig.getLong("snowflake.center.id", 0)));

    protected NextFlow() {
        // 搜索默认实现：
        // 1. AT指令系统
        findIfExists("com.github.yoojia.flow.at.AtSystemInterceptor",
                (Consumer<EventInterceptor>) this::install);

        // 安装默认实现：
        // 1. 本地设备事件派发器
        install(new LocalDeviceEventDelivery());
    }

    //// Context Impl

    @Override
    public long nextSequenceId() {
        return mSnowflakeId.getChecked().nextId();
    }

    @Override
    public Event requestEvent(Event request) {
        // 每个Request自动设置序列ID
        final Required<Event> output = new Required<>();
        final Dispatcher.OnEventReceivedListener listener = (event) -> {
            if (event.sequence != 0 && request.sequence == event.sequence) {
                output.set(event);
            }
        };
        mDispatcher.addOnEventReceivedListener(listener);
        mDispatcher.enqueueEvent(request);
        try {
            return output.getCheckedAwait(
                    mSystemConfig.getLong("request.timeout", 10), TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            LOGGER.error("Request timeout", e);
            return Event.simple(request.sequence, Context.TOPIC_SYS_TIMEOUT);
        } finally {
            mDispatcher.removeOnEventReceivedListener(listener);
        }
    }

    @Override
    public void publishEvent(Event event) {
        mDispatcher.enqueueEvent(event);
    }

    @Override
    public VirtualDevice findLocalDevice(String address) {
        return mDevices.stream()
                .filter(dev ->
                        dev.getAddress().equals(address))
                .findFirst()
                .orElse(null);
    }

    @Override
    public Config getGlobalConfig() {
        return mGlobalConfig;
    }

    @Override
    public Set<String> getDeviceAddresses() {
        return mDevices.stream()
                .map(VirtualDevice::getAddress)
                .collect(Collectors.toSet());
    }

    @Override
    public List<VirtualDevice> getVirtualDevices() {
        // 返回一个新的虚拟设备列表
        return new ArrayList<>(mDevices);
    }

    /**
     * 启动
     */
    public void start() {
        // System config
        mSystemConfig.copy(mGlobalConfig.getConfig(FIELD_NAME_SYSTEM));

        LOGGER.info("---> Flow Service START...");
        // Start
        mDispatcher.onStart();
        mDeliveries.parallelStream()
                .forEach(EventDelivery::onStart);
        mDevices.parallelStream()
                .forEach(VirtualDevice::onStart);
        mPlugins.parallelStream()
                .forEach(Plugin::onStart);
    }

    /**
     * 停止
     */
    public void stop() {
        LOGGER.info("---> Flow Service STOP...");
        // Stop
        mPlugins.parallelStream()
                .forEach(Plugin::onStop);
        mDevices.parallelStream()
                .forEach(VirtualDevice::onStop);
        mDeliveries.parallelStream()
                .forEach(EventDelivery::onStop);
        mDispatcher.onStop();
    }

    ////

    /**
     * 安装事件转换处理器
     *
     * @param transformer EventPipeline
     * @return GeneralFlow
     */
    public NextFlow install(EventPipeline transformer) {
        mPipelines.add(transformer);
        return this;
    }

    public NextFlow uninstall(EventPipeline transformer) {
        mPipelines.remove(transformer);
        return this;
    }

    /**
     * 安装事件拦截处理器
     *
     * @param interceptor EventInterceptor
     * @return GeneralFlow
     */
    public NextFlow install(EventInterceptor interceptor) {
        mInterceptors.add(interceptor);
        return this;
    }

    public NextFlow uninstall(EventInterceptor interceptor) {
        mInterceptors.remove(interceptor);
        return this;
    }

    /**
     * 安装事件动作执行器
     *
     * @param handler EventHandler
     * @return GeneralFlow
     */
    public NextFlow install(EventHandler handler) {
        mHandlers.add(handler);
        return this;
    }

    public NextFlow uninstall(EventHandler handler) {
        mHandlers.remove(handler);
        return this;
    }

    /**
     * 安装事件派发处理器
     *
     * @param delivery EventDelivery
     * @return GeneralFlow
     */
    public NextFlow install(EventDelivery delivery) {
        mDeliveries.add(delivery);
        return this;
    }

    public NextFlow uninstall(EventDelivery delivery) {
        mDeliveries.remove(delivery);
        return this;
    }

    /**
     * 安装插件
     *
     * @param plugin Plugin
     * @return GeneralFlow
     */
    public NextFlow install(Plugin plugin) {
        mPlugins.add(plugin);
        return this;
    }

    public NextFlow uninstall(Plugin plugin) {
        mPlugins.remove(plugin);
        return this;
    }

    /**
     * 安装虚拟设备
     *
     * @param device VirtualDevice
     * @return GeneralFlow
     */
    public NextFlow install(VirtualDevice device) {
        final String address = device.getAddress();
        if (findLocalDevice(address) != null) {
            throw new IllegalArgumentException("Address for device MUST BE Unique, duplicated：" + address);
        }
        mDevices.add(device);
        return this;
    }

    public NextFlow uninstall(VirtualDevice device) {
        mDevices.remove(device);
        return this;
    }

}
